using Acme.BookStore.Purchase.Customergoods;
using Acme.BookStore.PurchaseDto.CustomergoodsDto;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
using System.Linq;
using Volo.Abp.Identity;
using System.Linq.Dynamic.Core;
using Volo.Abp.Caching;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;

namespace Acme.BookStore.Purchase.Customergoods
{
    public class CustomergoodsService : CrudAppService<
        CustomergoodsModel,
        CustomergoodsDto,
        Guid,
        PagedAndSortedResultRequestDto,
        CreateUpdateCustomergoodsDto>,
        ICustomergoodsService
    {
        private readonly IRepository<IdentityUser, Guid> _userRepository;
        private readonly IDistributedCache<PagedResultDto<CustomergoodsDto>, string> _customergoodsCache;
        private readonly ILogger<CustomergoodsService> _logger;

        public CustomergoodsService(
            IRepository<CustomergoodsModel, Guid> repository,
            IRepository<IdentityUser, Guid> userRepository,
            IDistributedCache<PagedResultDto<CustomergoodsDto>, string> customergoodsCache,
            ILogger<CustomergoodsService> logger)
            : base(repository)
        {
            _userRepository = userRepository;
            _customergoodsCache = customergoodsCache;
            _logger = logger;
        }

        public override async Task<CustomergoodsDto> GetAsync(Guid id)
        {
            var cacheKey = $"customergoods:{id}";
            
            // Get from database directly since we're caching the list, not individual items
            return await base.GetAsync(id);
        }

        private async Task InvalidateCache()
        {
            try
            {
                _logger.LogInformation("Invalidating Customergoods cache");
                // 使用通配符删除所有相关缓存
                await _customergoodsCache.RemoveAsync("CustomerGoods:*");
                _logger.LogInformation("Customergoods cache invalidated successfully");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error invalidating Customergoods cache");
            }
        }

        public override async Task<CustomergoodsDto> CreateAsync(CreateUpdateCustomergoodsDto input)
        {
            var result = await base.CreateAsync(input);
            await InvalidateCache();
            return result;
        }

        public override async Task<CustomergoodsDto> UpdateAsync(Guid id, CreateUpdateCustomergoodsDto input)
        {
            var result = await base.UpdateAsync(id, input);
            await InvalidateCache();
            return result;
        }

        public override async Task DeleteAsync(Guid id)
        {
            await base.DeleteAsync(id);
            await InvalidateCache();
        }

        private string GetListCacheKey(PagedAndSortedResultRequestDto input)
        {
            return $"CustomerGoods:{nameof(GetListAsync)}:pageSize:{input.MaxResultCount}:pageNum:{input.SkipCount / input.MaxResultCount}:sorting:{input.Sorting}";
        }

        private string GetConditionsCacheKey(CustomgoodsQueryDto input)
        {
            return $"CustomerGoods:{nameof(GetListByConditionsAsync)}:" +
                   $"po:{input.PurchaseOrderNo ?? "all"}:" +
                   $"co:{input.CustomerOrderNo ?? "all"}:" +
                   $"car:{input.CarNo ?? "all"}:" +
                   $"driver:{input.DriverName ?? "all"}:" +
                   $"phone:{input.DriverPhone ?? "all"}:" +
                   $"status:{input.Status ?? "all"}:" +
                   $"dateFrom:{input.ExpectedArrivalDateFrom?.ToString("yyyyMMdd") ?? "all"}:" +
                   $"dateTo:{input.ExpectedArrivalDateTo?.ToString("yyyyMMdd") ?? "all"}:" +
                   $"pageSize:{input.MaxResultCount}:" +
                   $"pageNum:{input.SkipCount / input.MaxResultCount}:" +
                   $"sorting:{input.Sorting}";
        }

        public override async Task<PagedResultDto<CustomergoodsDto>> GetListAsync(PagedAndSortedResultRequestDto input)
        {
            var cacheKey = GetListCacheKey(input);
            
            // Try to get from cache
            var cachedList = await _customergoodsCache.GetAsync(cacheKey);
            if (cachedList != null)
            {
                _logger.LogInformation($"Cache hit for key: {cacheKey}");
                return cachedList;
            }
            _logger.LogInformation($"Cache miss for key: {cacheKey}");

            var queryable = await Repository.GetQueryableAsync();
            var userQueryable = await _userRepository.GetQueryableAsync();

            var query = from customergoods in queryable
                       join user in userQueryable on customergoods.CreatorId equals user.Id into userJoin
                       from user in userJoin.DefaultIfEmpty()
                       select new CustomergoodsDto
                       {
                           Id = customergoods.Id,
                           PurchaseOrderNo = customergoods.PurchaseOrderNo,
                           SupplierCode = customergoods.SupplierCode,
                           CarNo = customergoods.CarNo,
                           DriverName = customergoods.DriverName,
                           DriverPhone = customergoods.DriverPhone,
                           Status = customergoods.Status,
                           Shipper = customergoods.Shipper,
                           EstimatedArrivalTime = customergoods.EstimatedArrivalTime,
                           CustomerOrderNo = customergoods.CustomerOrderNo,
                           TransportNo = customergoods.TransportNo,
                           TransportCompany = customergoods.TransportCompany,
                           Supplier = customergoods.Supplier,
                           ReservedWarehouseArea = customergoods.ReservedWarehouseArea,
                           Warehouse = customergoods.Warehouse,
                           OrderType = customergoods.OrderType,
                           Remarks = customergoods.Remarks,
                           AttachmentPath = customergoods.AttachmentPath,
                           CreatorId = customergoods.CreatorId,
                           CreationTime = customergoods.CreationTime,
                           LastModifierId = customergoods.LastModifierId,
                           LastModificationTime = customergoods.LastModificationTime,
                           CreatorName = user != null ? user.UserName : string.Empty
                       };

            var totalCount = await AsyncExecuter.CountAsync(query);

            var items = await AsyncExecuter.ToListAsync(
                query.OrderBy(input.Sorting ?? nameof(CustomergoodsModel.Id)).PageBy(input)
            );

            var result = new PagedResultDto<CustomergoodsDto>(totalCount, items);

            // Save to cache with default expiration
            await _customergoodsCache.SetAsync(
                cacheKey,
                result,
                CustomergoodsCacheOptions.GetDefaultOptions()
            );

            return result;
        }

        public async Task<ListResultDto<ICustomergoodsService.OrderTypeModelDto>> GetOrderTypeModelAsync()
        {
            var list = Enum.GetValues(typeof(OrderTypeModel))
                .Cast<OrderTypeModel>()
                .Select(x => new ICustomergoodsService.OrderTypeModelDto
                {
                    Id = (int)x,
                    Name = x.ToString()
                }).ToList();

            return new ListResultDto<ICustomergoodsService.OrderTypeModelDto>(list);
        }

        public async Task<ListResultDto<ICustomergoodsService.ReservedWarehouseAreaModelDto>> GetReservedWarehouseAreaModelAsync()
        {
            var list = Enum.GetValues(typeof(ReservedWarehouseAreaModel))
                .Cast<ReservedWarehouseAreaModel>()
                .Select(x => new ICustomergoodsService.ReservedWarehouseAreaModelDto
                {
                    Id = (int)x,
                    Name = x.ToString()
                }).ToList();

            return new ListResultDto<ICustomergoodsService.ReservedWarehouseAreaModelDto>(list);
        }

        public async Task<ListResultDto<ICustomergoodsService.WarehouseModelDto>> GetWarehouseModelAsync()
        {
            var list = Enum.GetValues(typeof(WarehouseModel))
                .Cast<WarehouseModel>()
                .Select(x => new ICustomergoodsService.WarehouseModelDto
                {
                    Id = (int)x,
                    Name = x.ToString()
                }).ToList();

            return new ListResultDto<ICustomergoodsService.WarehouseModelDto>(list);
        }

        public async Task<PagedResultDto<CustomergoodsDto>> GetListByConditionsAsync(CustomgoodsQueryDto input)
        {
            var cacheKey = GetConditionsCacheKey(input);
            
            // Try to get from cache
            var cachedResult = await _customergoodsCache.GetAsync(cacheKey);
            if (cachedResult != null)
            {
                _logger.LogInformation($"Cache hit for key: {cacheKey}");
                return cachedResult;
            }
            _logger.LogInformation($"Cache miss for key: {cacheKey}");

            var queryable = await Repository.GetQueryableAsync();
            var userQueryable = await _userRepository.GetQueryableAsync();

            var query = from customergoods in queryable
                       join user in userQueryable on customergoods.CreatorId equals user.Id into userJoin
                       from user in userJoin.DefaultIfEmpty()
                       where (string.IsNullOrWhiteSpace(input.PurchaseOrderNo) || customergoods.PurchaseOrderNo.Contains(input.PurchaseOrderNo))
                             && (string.IsNullOrWhiteSpace(input.CustomerOrderNo) || customergoods.CustomerOrderNo.Contains(input.CustomerOrderNo))
                             && (string.IsNullOrWhiteSpace(input.CarNo) || customergoods.CarNo.Contains(input.CarNo))
                             && (string.IsNullOrWhiteSpace(input.DriverName) || customergoods.DriverName.Contains(input.DriverName))
                             && (string.IsNullOrWhiteSpace(input.DriverPhone) || customergoods.DriverPhone.Contains(input.DriverPhone))
                             && (string.IsNullOrWhiteSpace(input.Status) || customergoods.Status.Contains(input.Status))
                             && (!input.ExpectedArrivalDateFrom.HasValue || customergoods.EstimatedArrivalTime >= input.ExpectedArrivalDateFrom)
                             && (!input.ExpectedArrivalDateTo.HasValue || customergoods.EstimatedArrivalTime <= input.ExpectedArrivalDateTo)
                       select new CustomergoodsDto
                       {
                           Id = customergoods.Id,
                           PurchaseOrderNo = customergoods.PurchaseOrderNo,
                           SupplierCode = customergoods.SupplierCode,
                           CarNo = customergoods.CarNo,
                           DriverName = customergoods.DriverName,
                           DriverPhone = customergoods.DriverPhone,
                           Status = customergoods.Status,
                           Shipper = customergoods.Shipper,
                           EstimatedArrivalTime = customergoods.EstimatedArrivalTime,
                           CustomerOrderNo = customergoods.CustomerOrderNo,
                           TransportNo = customergoods.TransportNo,
                           TransportCompany = customergoods.TransportCompany,
                           Supplier = customergoods.Supplier,
                           ReservedWarehouseArea = customergoods.ReservedWarehouseArea,
                           Warehouse = customergoods.Warehouse,
                           OrderType = customergoods.OrderType,
                           Remarks = customergoods.Remarks,
                           AttachmentPath = customergoods.AttachmentPath,
                           CreatorId = customergoods.CreatorId,
                           CreationTime = customergoods.CreationTime,
                           LastModifierId = customergoods.LastModifierId,
                           LastModificationTime = customergoods.LastModificationTime,
                           CreatorName = user != null ? user.UserName : string.Empty
                       };

            var totalCount = await AsyncExecuter.CountAsync(query);

            var items = await AsyncExecuter.ToListAsync(
                query.OrderBy(input.Sorting ?? nameof(CustomergoodsModel.Id)).PageBy(input)
            );

            var result = new PagedResultDto<CustomergoodsDto>(totalCount, items);

            // Save to cache with conditions expiration
            await _customergoodsCache.SetAsync(
                cacheKey,
                result,
                CustomergoodsCacheOptions.GetDefaultOptions(CustomergoodsCacheOptions.ConditionsCacheDuration)
            );

            return result;
        }
    }
} 